package media

import (
	"bytes"
	"crypto/sha256"
	"encoding/gob"
	"encoding/hex"
	"go.etcd.io/bbolt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

const (
	MaxLine = 10
)

type Media struct {
	Path string
	Name string
}

// key: hash
// value: {name, path}
type _Media struct {
	hash string
	path string
	name string
}

func Hash(str string) string {
	hash := sha256.New()
	hash.Write([]byte(str))
	return hex.EncodeToString(hash.Sum(nil))
}

func GobEncode(obj interface{}) []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(obj)
	check(err)
	return buf.Bytes()
}

func GobDecode(data []byte, obj interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(obj)
}

func insert(db *bbolt.DB, class string, mediaList []_Media) {
	err := db.Update(func(tx *bbolt.Tx) error {
		mb, err := tx.CreateBucketIfNotExists([]byte("media:" + class))
		check(err)

		ib, err := tx.CreateBucketIfNotExists([]byte("index"))
		check(err)

		for _, media := range mediaList {
			value := Media{
				Name: media.name,
				Path: media.path,
			}
			mb.Put([]byte(media.path), GobEncode(value))
			ib.Put([]byte(media.hash), []byte(media.path))
		}
		return nil
	})
	check(err)
}

func walkClass(db *bbolt.DB, classDir, class string) {
	var mediaList []_Media
	count := 0

	err := filepath.Walk(classDir, func(path string, info os.FileInfo, err error) error {
		check(err)

		if info.IsDir() {
			// fmt.Println("目录修改时间:", info.ModTime())
		} else {
			absPath, err := filepath.Abs(path)
			check(err)

			ext := filepath.Ext(info.Name())
			raw := strings.TrimSuffix(info.Name(), ext)

			mediaList = append(mediaList, _Media{
				hash: Hash(absPath),
				path: absPath,
				name: raw,
			})

			count++
			if (count % MaxLine) == 0 {
				insert(db, class, mediaList)
			}

			// fmt.Println("abs:", absPath)
			// fmt.Println("name:", mediaList[count-1].name)
		}

		return nil
	})
	check(err)

	insert(db, class, mediaList)

	if class[0] != '.' {
		log.Printf("video: [%s] x %d\n", class, count)
	} else {
		log.Printf("video: [%s] x %d\n", "***", count)
	}
}

func walkRoot(db *bbolt.DB, root string) {
	entries, err := os.ReadDir(root)
	if err != nil {
		log.Fatal(err)
	}

	for _, entry := range entries {
		if entry.IsDir() {
			// each dir in root is a class
			walkClass(db, root+"/"+entry.Name(), entry.Name())
		}
	}
}

// @path: from toml, basename is video class
func InitDB(db *bbolt.DB, paths []string) {
	if paths == nil {
		log.Println("No media paths found!")
		return
	}

	for _, path := range paths {
		info, err := os.Stat(path)
		if err != nil {
			log.Println("[Warn] path not exist:", path)
			continue
		}
		if info.IsDir() {
			walkRoot(db, path)
		} else {
		}
	}
}
